iT邦幫忙

2026 iThome 鐵人賽

DAY 9
0
Vibe Coding

從零打造 AI 全端應用:Vibe Coding 結合 n8n 視覺化工作流實戰系列 第 9 篇

Day 09:視覺打磨與優化:實作 Loading 轉場與 AI 推薦結果卡片

  • 分享至 

  • xImage
  •  

Day 09:視覺打磨與優化:實作 Loading 轉場與 AI 推薦結果卡片

嗨,大家今天過得好嗎?歡迎來到鐵人賽 Day 9。

昨天我們成功在前端把 APIService 的假資料(Mock Data)接上了 Controller,現在只要按下按鈕,我們的 isLoading 狀態就會變成 true,兩秒後又會把資料塞進 resultMenu 裡。

資料流是通了,但如果你現在去按按鈕,畫面上什麼反應都沒有(只有 Console 在默默印字)。這在 UX(使用者體驗)上是絕對不合格的。特別是未來我們要串接真正的 LLM,AI 生成文字通常需要 3 到 5 秒甚至更久,如果沒有「轉圈圈」動畫,使用者一定會以為 App 當機了,然後瘋狂連點按鈕。

今天,我們就要來用 Vibe Coding 完成前端介面的最後一塊拼圖:讀取狀態與結果卡片。

Vibe Coding 切版法:分而治之

老規矩,我們絕對不對 AI 說:「幫我把 Loading 畫面跟結果畫面加進去」。如果你這樣說,AI 保證會用一堆 if-else 把你的 ContentView 塞爆。

我們要繼續發揮昨天「元件化」的精神。把設計需求拆成兩個部分:

  1. 結果卡片 (ResultCardView): 專門用來顯示 HardwareMenu 的精美 UI。
  2. 主畫面狀態切換: 根據 Controller 的狀態來決定要顯示表單、Loading 還是結果卡片,並加上平滑的轉場動畫。

第一步:請 AI 刻出 ResultCardView

打開你的 Prompt 視窗,下這段指令:

請幫我用 SwiftUI 寫一個獨立的 `ResultCardView.swift` 元件。
它會接收我昨天定義的 `HardwareMenu` 型別資料。

設計要求:
1. 採用卡片式設計,白色背景、圓角、帶有輕微陰影。
2. 頂部要有個標題「 你的專屬 AI 菜單」。
3. 清楚列出 CPU 與 GPU 的型號,可以用 HStack 搭配圖示 (SF Symbols)。
4. 總價 (totalPrice) 要特別放大並用醒目的顏色標示。
5. 最下方用一個淺色背景的區塊,顯示 AI 的推薦理由 (reason)。

拿到的程式碼請放進 Views 資料夾,大概會長這樣,非常乾淨:

// ResultCardView.swift
import SwiftUI

struct ResultCardView: View {
    let menu: HardwareMenu
    
    var body: some View {
        VStack(alignment: .leading, spacing: 16) {
            Text(" 你的專屬 AI 菜單")
                .font(.title2)
                .fontWeight(.bold)
                .foregroundColor(.purple)
            
            Divider()
            
            // 硬體清單
            VStack(alignment: .leading, spacing: 12) {
                HStack {
                    Image(systemName: "cpu")
                        .foregroundColor(.blue)
                    Text("CPU: \(menu.cpu)")
                        .font(.headline)
                }
                
                HStack {
                    Image(systemName: "memorychip")
                        .foregroundColor(.green)
                    Text("GPU: \(menu.gpu)")
                        .font(.headline)
                }
            }
            
            // 總價
            HStack {
                Text("總金額預估:")
                    .foregroundColor(.gray)
                Spacer()
                Text("NT$ \(menu.totalPrice)")
                    .font(.title)
                    .fontWeight(.heavy)
                    .foregroundColor(.red)
            }
            .padding(.vertical, 8)
            
            // AI 推薦理由
            VStack(alignment: .leading, spacing: 8) {
                Text("💡 AI 推薦理由")
                    .font(.caption)
                    .fontWeight(.bold)
                    .foregroundColor(.gray)
                Text(menu.reason)
                    .font(.subheadline)
                    .foregroundColor(.secondary)
                    .fixedSize(horizontal: false, vertical: true)
            }
            .padding()
            .background(Color.blue.opacity(0.05))
            .cornerRadius(10)
        }
        .padding()
        .background(Color.white)
        .cornerRadius(15)
        .shadow(color: Color.black.opacity(0.1), radius: 10, x: 0, y: 5)
    }
}

第二步:把 Loading 與轉場動畫加進主畫面

元件準備好了,接下來就是修改 ContentView,把狀態串起來。你可以自己改,或是叫 AI 幫你加上 ProgressView 和動畫。

將 ContentView 更新為以下結構:

// ContentView.swift
import SwiftUI

struct ContentView: View {
    @StateObject private var controller = HomeController()
    
    var body: some View {
        NavigationView {
            ZStack {
                Color(.systemGray6).edgesIgnoringSafeArea(.all)
                
                ScrollView {
                    VStack(spacing: 25) {
                        
                        // 1. 輸入表單區塊
                        InputCardView(
                            requestData: $controller.requestData,
                            purposeOptions: controller.purposeOptions
                        )
                        
                        // 2. 狀態判斷與結果渲染
                        if controller.isLoading {
                            // 讀取中的畫面
                            VStack {
                                ProgressView()
                                    .scaleEffect(1.5)
                                    .padding()
                                Text("AI 正在思考最適合你的配置...")
                                    .foregroundColor(.gray)
                            }
                            .padding(.top, 20)
                            
                        } else if let menu = controller.resultMenu {
                            // 成功拿到資料,顯示結果卡片
                            ResultCardView(menu: menu)
                                .transition(.scale.combined(with: .opacity)) // 加入縮放與淡入動畫
                        }
                        
                        Spacer()
                        
                        // 3. 送出按鈕 (如果正在讀取就 Disable 避免連點)
                        Button(action: {
                            controller.submitRequest()
                        }) {
                            Text(controller.isLoading ? "運算中..." : "產生推薦菜單")
                                .font(.headline)
                                .foregroundColor(.white)
                                .frame(maxWidth: .infinity)
                                .padding()
                                .background(
                                    LinearGradient(
                                        gradient: Gradient(colors: controller.isLoading ? [Color.gray, Color.gray] : [Color.blue, Color.purple]),
                                        startPoint: .leading, endPoint: .trailing
                                    )
                                )
                                .cornerRadius(12)
                        }
                        .disabled(controller.isLoading) // 讀取時禁用按鈕
                        .padding(.bottom, 20)
                    }
                    .padding()
                }
            }
            .navigationTitle("AI 硬體配對")
            // 讓畫面的出現與消失都有平滑的過渡動畫
            .animation(.easeInOut(duration: 0.3), value: controller.isLoading)
            .animation(.spring(), value: controller.resultMenu != nil)
        }
    }
}

來測試一下吧!

現在,按下 Cmd + R 把模擬器跑起來。

輸入預算,選擇用途,點下「產生推薦菜單」。你會看到按鈕變成灰色的「運算中...」,畫面上出現轉圈圈動畫與提示文字;兩秒後(Mock Data 的延遲結束),轉圈圈消失,一張精美、帶有 AI 推薦理由的卡片會用充滿彈性的動畫(Spring Animation)彈出來。

太感人了,我們的前端 MVP(最小可行性產品)視覺與互動已經完全到位!

小結

這幾天下來,我們利用 Vibe Coding 極速完成了 iOS 的介面、分離了 MVC 架構、實作了 API Service、甚至搞定了非同步的狀態轉換與動畫。這就是我在 Day 1 提到的「雙效加速器」中,屬於前端的飆速快感。

但在我們把戰場正式切換回 n8n 寫後端之前,明天我想先暫停一下。我們來聊聊這幾天 Vibe Coding 實戰中,「如果不懂基礎語法,你會踩到哪些致命的雷?又該如何 Debug?」 這是一篇送給所有依賴 AI 開發者的心法指南。

我們 Day 10 見!


上一篇
Day 08:串接前夕:設計前端 API Service 層與 Mock Data 測試
下一篇
Day 10:Vibe Coding 的致命傷:不懂基礎語法會踩到的雷與 Debug 心法
系列文
從零打造 AI 全端應用:Vibe Coding 結合 n8n 視覺化工作流實戰 共 17 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言